﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
using AspectCore.Extensions.DependencyInjection;
using AspectCore.Configuration;
using RedLockNet;
using System.Linq;
using DotNetCore.CAP;
using Microsoft.Extensions.Caching.Distributed;

namespace Handday.Erp.Core
{
    /// <summary>
    /// AspectExtensions
    /// </summary>
    public static class AspectExtensions
    {
        /// <summary>
        /// 为服务添加ASPECT
        /// </summary>
        public static IServiceCollection AddAspectService(this IServiceCollection services)
        {
            services.ConfigureDynamicProxy(config =>
            {
                config.Interceptors.AddTyped<CapInterceptor>(Predicates.ForNameSpace("Handday.Erp.Transfer.Subscriber"));
            });

            return services;
        }
    }

    /// <summary>
    /// cap interceptor
    /// </summary>
    public class CapInterceptor : AbstractInterceptor
    {
        /// <summary>
        /// invoke
        /// </summary>
        public async override Task Invoke(AspectContext context, AspectDelegate next)
        {
            using (var scope = context.ServiceProvider.CreateScope())
            {
                if (context.Parameters.FirstOrDefault(m => m is CapHeader) is CapHeader capHeader)
                {
                    var lockKey = $"{capHeader["cap-msg-group"]}_{capHeader["cap-msg-id"]}";
                    var cacheKey = $"cap.msg:{capHeader["cap-msg-group"]}:{capHeader["cap-msg-id"]}";
                    var lockFactory = context.ServiceProvider.GetRequiredService<IDistributedLockFactory>();
                    var cache = context.ServiceProvider.GetService<IDistributedCache>();
                    await using (var redisLock = await lockFactory.CreateLockAsync(lockKey, TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(2)))
                    {
                        if (cache != null && cache.GetString(cacheKey) == "1")
                        {
                            return;
                        }

                        if (!redisLock.IsAcquired)
                        {
                            throw new Exception("the method is processing");
                        }

                        await next(context);
                        cache?.SetString(cacheKey, "1", new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = System.TimeSpan.FromHours(20) });
                    }
                }
                else
                {
                    await next(context);
                }
            }
        }
    }
}